package org.example.ding.service;

import org.example.ding.dto.DishDto;
import org.example.ding.entity.Dish;
import org.example.ding.entity.DishCategory;
import org.example.ding.repository.DishRepository;
import org.example.ding.repository.DishCategoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 菜品服务层
 */
@Service
@Transactional
public class DishService {

    @Autowired
    private DishRepository dishRepository;

    @Autowired
    private DishCategoryRepository dishCategoryRepository;

    /**
     * 创建菜品
     */
    public Dish createDish(DishDto dishDto) {
        // 检查菜品名称是否已存在
        if (dishRepository.existsByName(dishDto.getName())) {
            throw new RuntimeException("菜品名称已存在");
        }

        // 获取分类
        DishCategory category = dishCategoryRepository.findById(dishDto.getCategoryId())
            .orElseThrow(() -> new RuntimeException("菜品分类不存在"));

        Dish dish = dishDto.toDish();
        dish.setCategory(category);
        
        // 设置默认值
        if (dish.getPopularityScore() == null) {
            dish.setPopularityScore(BigDecimal.ZERO);
        }
        if (dish.getOrderCount() == null) {
            dish.setOrderCount(0);
        }

        return dishRepository.save(dish);
    }

    /**
     * 更新菜品
     */
    public Dish updateDish(Long dishId, DishDto dishDto) {
        Dish dish = dishRepository.findById(dishId)
            .orElseThrow(() -> new RuntimeException("菜品不存在"));

        // 如果名称发生变化，检查新名称是否已被使用
        if (dishDto.getName() != null && !dishDto.getName().equals(dish.getName())) {
            if (dishRepository.existsByName(dishDto.getName())) {
                throw new RuntimeException("菜品名称已被其他菜品使用");
            }
        }

        // 如果分类发生变化，更新分类
        if (dishDto.getCategoryId() != null && 
            (dish.getCategory() == null || !dishDto.getCategoryId().equals(dish.getCategory().getId()))) {
            DishCategory category = dishCategoryRepository.findById(dishDto.getCategoryId())
                .orElseThrow(() -> new RuntimeException("菜品分类不存在"));
            dish.setCategory(category);
        }

        // 更新菜品信息
        dishDto.updateDish(dish);

        return dishRepository.save(dish);
    }

    /**
     * 删除菜品（软删除）
     */
    public void deleteDish(Long dishId) {
        Dish dish = dishRepository.findById(dishId)
            .orElseThrow(() -> new RuntimeException("菜品不存在"));
        
        dish.setIsAvailable(false);
        dishRepository.save(dish);
    }

    /**
     * 恢复菜品
     */
    public void restoreDish(Long dishId) {
        Dish dish = dishRepository.findById(dishId)
            .orElseThrow(() -> new RuntimeException("菜品不存在"));
        
        dish.setIsAvailable(true);
        dishRepository.save(dish);
    }

    /**
     * 根据ID查找菜品
     */
    @Transactional(readOnly = true)
    public Optional<Dish> findById(Long dishId) {
        return dishRepository.findById(dishId);
    }

    /**
     * 根据ID查找可用菜品
     */
    @Transactional(readOnly = true)
    public Optional<Dish> findAvailableById(Long dishId) {
        return dishRepository.findById(dishId)
            .filter(dish -> dish.getIsAvailable() != null && dish.getIsAvailable());
    }

    /**
     * 获取所有可用菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> getAllAvailableDishes() {
        return dishRepository.findByIsAvailableTrue();
    }

    /**
     * 根据分类获取菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> getDishesByCategory(Long categoryId) {
        return dishRepository.findByCategoryIdAndIsAvailableTrue(categoryId);
    }

    /**
     * 获取热门菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> getPopularDishes(int limit) {
        List<Dish> dishes = dishRepository.findByIsAvailableTrueOrderByPopularityScoreDesc();
        return dishes.stream().limit(limit).collect(Collectors.toList());
    }

    /**
     * 获取最多点餐的菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> getMostOrderedDishes() {
        return dishRepository.findTop10ByIsAvailableTrueOrderByOrderCountDesc();
    }

    /**
     * 搜索菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> searchDishes(String keyword) {
        return dishRepository.searchByName(keyword);
    }

    /**
     * 复合搜索菜品
     */
    @Transactional(readOnly = true)
    public Page<Dish> searchDishes(Long categoryId, String keyword, 
                                  Dish.SpiceLevel spiceLevel, Dish.CookingMethod cookingMethod,
                                  BigDecimal minCalories, BigDecimal maxCalories,
                                  Pageable pageable) {
        return dishRepository.searchDishes(categoryId, keyword, spiceLevel, cookingMethod,
                                         minCalories, maxCalories, pageable);
    }

    /**
     * 分页获取菜品
     */
    @Transactional(readOnly = true)
    public Page<Dish> getDishes(Pageable pageable) {
        return dishRepository.findByIsAvailableTrue(pageable);
    }

    /**
     * 根据分类分页获取菜品
     */
    @Transactional(readOnly = true)
    public Page<Dish> getDishesByCategory(Long categoryId, Pageable pageable) {
        return dishRepository.findByCategoryIdAndIsAvailableTrue(categoryId, pageable);
    }

    /**
     * 根据营养成分范围查找菜品
     */
    @Transactional(readOnly = true)
    public List<Dish> findDishesByNutrition(BigDecimal minCalories, BigDecimal maxCalories,
                                           BigDecimal minProtein, BigDecimal maxProtein) {
        List<Dish> calorieFiltered = dishRepository.findByCaloriesRange(minCalories, maxCalories);
        return calorieFiltered.stream()
            .filter(dish -> dish.getProtein().compareTo(minProtein) >= 0 && 
                           dish.getProtein().compareTo(maxProtein) <= 0)
            .collect(Collectors.toList());
    }

    /**
     * 增加菜品点餐次数
     */
    public void incrementOrderCount(Long dishId) {
        Dish dish = dishRepository.findById(dishId)
            .orElseThrow(() -> new RuntimeException("菜品不存在"));
        
        dish.incrementOrderCount();
        dishRepository.save(dish);
    }

    /**
     * 批量增加菜品点餐次数
     */
    public void incrementOrderCounts(List<Long> dishIds) {
        List<Dish> dishes = dishRepository.findByIdInAndIsAvailableTrue(dishIds);
        dishes.forEach(Dish::incrementOrderCount);
        dishRepository.saveAll(dishes);
    }

    /**
     * 获取菜品统计信息
     */
    @Transactional(readOnly = true)
    public DishStatistics getDishStatistics() {
        Long totalCount = dishRepository.countAvailableDishes();
        List<Object[]> categoryStats = dishRepository.countDishesByCategory();
        Object[] nutritionStats = dishRepository.getNutritionStatistics();
        
        return new DishStatistics(totalCount, categoryStats, nutritionStats);
    }

    /**
     * 检查菜品名称是否可用
     */
    @Transactional(readOnly = true)
    public boolean isDishNameAvailable(String name) {
        return !dishRepository.existsByName(name);
    }

    /**
     * 检查菜品名称是否可用（排除指定ID）
     */
    @Transactional(readOnly = true)
    public boolean isDishNameAvailable(String name, Long excludeId) {
        Optional<Dish> existingDish = dishRepository.findByName(name);
        return existingDish.isEmpty() || existingDish.get().getId().equals(excludeId);
    }



    /**
     * 菜品统计信息内部类
     */
    public static class DishStatistics {
        private final Long totalCount;
        private final List<Object[]> categoryStats;
        private final Object[] nutritionStats;

        public DishStatistics(Long totalCount, List<Object[]> categoryStats, Object[] nutritionStats) {
            this.totalCount = totalCount;
            this.categoryStats = categoryStats;
            this.nutritionStats = nutritionStats;
        }

        public Long getTotalCount() { return totalCount; }
        public List<Object[]> getCategoryStats() { return categoryStats; }
        public Object[] getNutritionStats() { return nutritionStats; }
    }
}
